// 动画的优化方案，attack_0.json对应attack_0.png的res
// 而动画放在 attack.json中或者 ani.json-"attack"中
// 这样各个方向和所有武器都共用这个ani.json
// 可以多做一栏编辑charAni!

// 然后再吧所有动画的.json打成一个zip包，主要有几M所以不推荐！
const ICON_HIT = 'shellopen_png';
const ICON_SOUND = 'eye_png';

// interface AniEvent{
// 	name: string;
// 	frame: number;
// 	index: number;
// }

enum AniEventEnum {
	Attack = 'attack',
	Hit = 'hit',
}

function isAniEvent(event: string) {
	const e = event.substr(1);
	return e === AniEventEnum.Attack || e === AniEventEnum.Hit;
}

interface AniTreeEvent {
	sound: string;
	vol: number;
	hit: string;
	// index: number;
}

class TreeAni extends TreeView {
	constructor() {
		super();
		this._name = 'ani';
	}

	override childrenCreated(): void {
		super.childrenCreated();
		this.defaultOption();
		// this.addEventListener(TreeView.saveEvent,this.onSave,this)
		// this.option.use_eye = true;
		// this.addEventListener(TreeView.eyeEvent,this.onEye,this)
		this.addEventListener(TreeView.spaceEvent, this.onSpace, this);
		this.loadSettingFile();
		this.addEventListener(mouse.MouseEvent.MOUSE_WHEEL, this.onMouseWheel, this);
	}

	onSpace(ev: egret.Event): void {
		const item: TreeViewItem = ev.data;
		if (item === this.getRoot()) {
			return;
		}
		if (item.isFolder()) {
			return;
		}
		MainFrame.ins().leftWin.showAttr();
	}

	loadSettingFile(): void {
		const drama = gamedata.Setting.ins().getValues(`files.${this._name}`);
		if (drama) {
			nextFrameNum(10, () => this.openAni(drama), null);
		} // wait second.
	}

	static openEvent = 'ENV_TREEANI_OPEN';

	static changeEvent = 'ENV_TREEANI_CHANGE';

	file: string;

	simplejson: MCFile;

	trySave(newfile: string): boolean {
		const json: MCFile = FsUtils.loadJson(newfile);
		if (!json) {
			return false;
		}
		if (json.mc && json.res) {
			// for (const key in json.mc) {
			for (const [key, des] of Object.entries(json.mc)) {
				const src = this.simplejson.mc[key];
				// const des = json.mc[key];
				if (src && des) {
					// let frames = des.frames;
					const cp: MCData = Utils.deepCopy(src);
					for (let i = 0; i < cp.frames.length; i++) {
						const resDes = des.frames[i];
						cp.frames[i].x = resDes.x;
						cp.frames[i].y = resDes.y;
					}
					json.mc[key] = cp;
				}
			}
			FsUtils.saveJson(newfile, json);
			return true;
		}
		return false;
	}

	onSave(): void {
		this.saveAni();
		if (!isShiftDown()) {
			ui.log(`save ani ${this.file}`);
		} else {
			ui.log(`save all dirani ${this.file}`);
			const file = path.basename(this.file);
			const dir = path.dirname(this.file);

			const s = file.split('_');
			if (s.length === 2) {
				const name = s[0];
				const dirnow = Number(s[1][0]);
				// ui.msglog(name,dirnow,s[0],s[1])
				for (let d = 0; d < 8; d++) {
					if (d !== dirnow) {
						const file2 = path.resolve(dir, `${name}_${d}.json`);
						this.trySave(file2);
					}
				}
				// ui.log("save ani " + this.file + " to all dir")
				// 武器等等也可以
			}
		}
	}

	getFileOp(): string[] {
		return ['save', 'open', 'shellopen'];
	}

	onFileOp(file: string, op: string): boolean {
		if (op === 'save') {
			this.onSave();
			return true;
		}
		if (op === 'open') {
			this.openAni(file);
			return true;
		}
		if (op === 'shellopen') {
			rawOpen(this.file);
			return true;
		}
		return false;
	}

	openAni(file: string): boolean {
		console.log('openAni', file);
		if (!file.endsWith('.json')) {
			// ui.msgline('only can open json file');
			// return false;
			file = `${getAssets() + file}.json`;
		}
		const data = FsUtils.loadJson(file);
		if (!data) {
			return false;
		}
		return this.openAniData(file, data);
	}

	openAniData(file: string, data: MCFile): boolean {
		if (data && data.mc) {
			this.file = file;
			this.simplejson = data;
			this.constructorAni('mc', data.mc);
			// this.enableRootSave()

			this.expandFirst();
			this.onOpened(file, data);
			return true;
		}

		ui.log('TreeAni.openAniData not a valid ani file', file);
		return false;
	}

	onOpened(file: string, data: MCFile): void {
		this.dispatchEventWith(TreeAni.openEvent, false, { file, data });
		gamedata.Setting.ins().updateKeys(`files.${this._name}`, file);
		getLeftWindow().addHistory(this._name, file);
	}

	saveAni(): boolean {
		const rt = FsUtils.saveJson(this.file, this.simplejson);
		if (rt) {
			// RES.destroyRes();
			getAniWindow().mc.refreshCache(this.file);
		}
		return rt;
	}

	constructorAni(name: string, d: { [key: string]: MCData }): void {
		const data = [];
		// for (const key in d) {
		for (const [key, _value] of Object.entries(d)) {
			const value = _value;
			// let text = `${key}(${value.frameRate})`
			const out = {
				content: key,
				param: value.frameRate,
				items: [],
				caption: key,
				key: 'frameRate',
				value,
			};
			let index = 0;
			for (const f of value.frames) {
				// let txt = `${f.res}(${f.duration})`
				const item: TreeItemData = {
					content: f.res,
					param: f.duration,
					caption: f.res,
					key: 'duration',
					value: f,
					foldericon: null,
				};
				const ev = this.getEvent(key, index);
				if (ev) {
					item.foldericon = ev.hit ? ICON_HIT : ICON_SOUND;
				}

				out.items.push(item);
				index++;
			}
			data.push(out);
		}
		this.constructorRoot(name, data);
	}

	onJsonChanged(data: any): void {
		// save it and display...
		this.dispatchEventWith(TreeAni.changeEvent, false, data);
	}

	onMouseWheel(ev: egret.Event): void {
		const cur = this.getSelectedItem();
		const { data } = cur;
		if (!data.value || !data.key) {
			return;
		}

		let v = data.value[data.key];

		const scale = ev.data;
		if (scale > 0) {
			v++;
		} else if (v > 1) {
			v--;
		}

		data.value[data.key] = v;
		cur.setParam(v);
		this.onJsonChanged(cur.data);
	}

	getStartFrame(ani: string, index: number): number {
		const { frames } = this.simplejson.mc[ani];
		return getStartFrame(frames, index);
	}

	getEventInit(ani: string, index: number): AniTreeEvent {
		return (
			this.getEvent(ani, index) || {
				sound: '',
				vol: 0.5,
				hit: '',
				// index,
			}
		);
	}

	getEvent(ani: string, index: number): AniTreeEvent {
		const { events } = this.simplejson.mc[ani];
		const i = events.filter((a) => a.index === index);
		if (i.length === 0) {
			return null;
		}
		const event: AniTreeEvent = {
			sound: '',
			vol: 0.5,
			hit: '',
			// index,
		};

		for (const e of i) {
			if (isAniEventHit(e)) {
				event.hit = '@hit';
			} else {
				const ss = e.name.split(':');
				event.sound = ss[0].substr(1);
				event.vol = Number(ss[1]);
			}
		}
		return event;
	}

	setAniEvent(cur: TreeViewItem, ani: string, index: number, event: AniTreeEvent): void {
		// let ev = this.getEvent(ani,frame)
		const { events } = this.simplejson.mc[ani];
		events.removeIf((a) => a.index === index);
		cur.setIcon('');

		if (event.hit || event.sound) {
			const start = this.getStartFrame(ani, index);
			ui.msglog('addMCEvent', ani, index, event);
			if (event.hit) {
				this.simplejson.mc[ani].events.push({ name: '@hit', frame: start, index });
			}
			if (event.sound) {
				const name = `@${event.sound}:${event.vol || 0.5}`;
				this.simplejson.mc[ani].events.push({ name, frame: start, index });
			}
			cur.setIcon(event.hit ? ICON_HIT : ICON_SOUND);
		}
	}

	override onShowAttr(cur: TreeViewItem): AniTreeEvent {
		if (cur.isFolder()) return null;
		const { data } = cur;
		const index = cur.getIndex();
		const father = cur.getParentItem();
		const ani = father.getContent();
		return this.getEventInit(ani, index);
	}

	override onAttrEnter(cur: TreeViewItem, item: AttrItem): boolean {
		if (cur.isFolder()) {
			return false;
		}
		const { data } = cur;

		const index = cur.getIndex();

		const father = cur.getParentItem();
		const ani = father.getContent();

		const v = item.getValueText();
		const k = item.data.key;

		const ev = this.getEventInit(ani, index);

		switch (k) {
			case 'sound':
				ev.sound = v;
				break;
			case 'hit':
				ev.hit = v;
				break;
			case 'vol':
				ev.vol = Number(v);
				break;
			default:
				return false;
		}

		// ui.msglog("modify event",ev.name,file2,vol,event)
		// ui.msglog('modify event', event);
		this.setAniEvent(cur, ani, index, ev);
		this.onJsonChanged(cur.data);
		return true;
	}
}
